#include "./BSP/REMOTE/bsp_remote.h"
#include "./SYSTEM/delay/delay.h"
#include <stdio.h>

/* ------------------------------------------------- 全局变量定义 ------------------------------------------------- */

TIM_HandleTypeDef g_tim5_handle;    /* TIM5定时器句柄 */

uint8_t g_remote_sta = 0;   /* 遥控器接收状态标志
                             * 位域定义：
                             * [7]: 收到引导码标志 (1=已收到引导码)
                             * [6]: 完成按键信息接收标志 (1=已获得完整按键信息)
                             * [5]: 保留位
                             * [4]: 上升沿捕获标志 (1=已捕获上升沿)
                             * [3:0]: 溢出计时器 (0-14计数)
                             */
uint32_t g_remote_data = 0; /* 红外接收到的数据，数据格式：[地址码][地址反码][命令码][命令反码] */
uint8_t g_remote_cnt = 0;   /* 按键按下的次数 */

/* ------------------------------------------------- 函数实现 ----------------------------------------------------- */

/**
 * @brief       红外遥控器初始化
 * @param       无
 * @return      无
 * @note        初始化红外接收引脚和定时器输入捕获功能
 *              配置定时器为1MHz计数频率，上升沿触发捕获
 *              使能输入捕获中断和更新中断
 */
void remote_init(void)
{
    TIM_IC_InitTypeDef tim_ic_init_handle;

    /* 配置定时器基本参数 */
    g_tim5_handle.Instance = REMOTE_IN_TIMX;                    /* 选择TIM5定时器 */
    g_tim5_handle.Init.Prescaler = (72 - 1);                    /* 预分频系数71，72MHz/72=1MHz，1us计数一次 */
    g_tim5_handle.Init.CounterMode = TIM_COUNTERMODE_UP;        /* 向上计数模式 */
    g_tim5_handle.Init.Period = 10000;                          /* 自动重装载值10000，10ms溢出周期 */
    g_tim5_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;  /* 时钟分频因子1，不分频 */
    
    /* 初始化定时器输入捕获功能 */
    HAL_TIM_IC_Init(&g_tim5_handle);
    
    /* 配置输入捕获通道参数 */
    tim_ic_init_handle.ICPolarity = TIM_ICPOLARITY_RISING;      /* 上升沿触发捕获 */
    tim_ic_init_handle.ICSelection = TIM_ICSELECTION_DIRECTTI;  /* 直接映射到TI2输入 */
    tim_ic_init_handle.ICPrescaler = TIM_ICPSC_DIV1;            /* 输入捕获不分频，每个边沿都捕获 */
    tim_ic_init_handle.ICFilter = 0x03;                         /* 输入滤波器，8个时钟周期滤波，抗干扰 */
    
    /* 配置TIM5通道2输入捕获 */
    HAL_TIM_IC_ConfigChannel(&g_tim5_handle, &tim_ic_init_handle, REMOTE_IN_TIMX_CHY);
    
    /* 启动输入捕获并使能中断 */
    HAL_TIM_IC_Start_IT(&g_tim5_handle, REMOTE_IN_TIMX_CHY);    /* 启动TIM5通道2输入捕获中断 */
    __HAL_TIM_ENABLE_IT(&g_tim5_handle, TIM_IT_UPDATE);         /* 使能定时器更新中断，用于超时检测 */
}

/**
 * @brief       定时器输入捕获底层初始化
 * @param       htim 定时器句柄指针
 * @return      无
 * @note        HAL库回调函数，由HAL_TIM_IC_Init()自动调用
 *              配置GPIO为复用输入模式，使能时钟和中断
 *              设置中断优先级为抢占优先级1，子优先级3
 */
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == REMOTE_IN_TIMX)
    {
        GPIO_InitTypeDef gpio_init_struct;
        
        /* 使能相关时钟 */
        REMOTE_IN_GPIO_CLK_ENABLE();                            /* 使能GPIOA时钟，红外接收引脚所在端口 */
        REMOTE_IN_TIMX_CHY_CLK_ENABLE();                        /* 使能TIM5定时器时钟 */
        
        /* 配置GPIO引脚为定时器复用功能 */
        gpio_init_struct.Pin = REMOTE_IN_GPIO_PIN;              /* PA1引脚 */
        gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;             /* 复用输入模式，连接到定时器 */
        gpio_init_struct.Pull = GPIO_PULLUP;                    /* 内部上拉，确保空闲时为高电平 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;          /* 高速模式，快速响应信号变化 */
        HAL_GPIO_Init(REMOTE_IN_GPIO_PORT, &gpio_init_struct);  /* 初始化PA1为TIM5_CH2功能 */

        /* 配置NVIC中断控制器 */
        HAL_NVIC_SetPriority(REMOTE_IN_TIMX_IRQn, 1, 3);        /* 设置中断优先级：抢占优先级1，子优先级3 */
        HAL_NVIC_EnableIRQ(REMOTE_IN_TIMX_IRQn);                /* 使能TIM5中断 */
    }

}

/**
 * @brief       定时器中断服务函数
 * @param       无
 * @return      无
 * @note        TIM5中断服务程序，调用HAL库通用中断处理函数
 *              处理输入捕获中断和更新中断
 */
void REMOTE_IN_TIMX_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&g_tim5_handle); /* 定时器共用处理函数 */
}

/**
 * @brief       定时器更新中断回调函数
 * @param       htim 定时器句柄指针
 * @return      无
 * @note        定时器溢出中断处理，用于超时检测
 *              当接收过程中发生超时时，清除相关标志位
 *              防止因信号干扰导致的接收状态异常
 */
//void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
//{
//    if (htim->Instance == REMOTE_IN_TIMX)
//    {
//        if (g_remote_sta & 0x80)                /* 检查是否已收到引导码 */
//        {
//            /* 清除上升沿捕获标志，防止误判 */
//            g_remote_sta &= ~0X10;              /* 清除bit4，取消上升沿已捕获标记 */

//            /* 检查是否完成32位数据接收 */
//            if ((g_remote_sta & 0X0F) == 0X00)
//            {
//                g_remote_sta |= 1 << 6;         /* 设置bit6，标记已完成一次按键信息采集 */
//            }
//            
//            /* 溢出计数器管理 */
//            if ((g_remote_sta & 0X0F) < 14)
//            {
//                g_remote_sta++;                 /* 溢出计数器递增，最大计数到14 */
//            }
//            else
//            {
//                /* 超时处理，重置接收状态 */
//                g_remote_sta &= ~(1 << 7);      /* 清除bit7，清空引导码标识 */
//                g_remote_sta &= 0XF0;           /* 清空低4位溢出计数器 */
//            }
//        }
//    }
//}

/**
 * @brief       定时器输入捕获中断回调函数
 * @param       htim 定时器句柄指针
 * @return      无
 * @note        处理红外信号的上升沿和下降沿捕获
 *              通过测量脉冲宽度解析NEC协议数据
 *              脉冲宽度标准：引导码4.5ms，数据位0为560us，数据位1为1680us
 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == REMOTE_IN_TIMX)
    {
        uint16_t dval;  /* 存储脉冲宽度值(单位：us) */

        if (RDATA)      /* 检测到上升沿 */ 
        {   
            /* 切换为下降沿捕获模式 */
            __HAL_TIM_SET_CAPTUREPOLARITY(&g_tim5_handle, REMOTE_IN_TIMX_CHY, TIM_INPUTCHANNELPOLARITY_FALLING);
            
            /* 重置计数器，开始测量脉冲宽度 */
            __HAL_TIM_SET_COUNTER(&g_tim5_handle, 0);   /* 清零计数器，准备测量高电平持续时间 */
            
            /* 标记上升沿已捕获 */
            g_remote_sta |= 0X10;                       /* 设置bit4，标记上升沿已被捕获 */
        }
        else           /* 检测到下降沿 */
        {
            /* 读取脉冲宽度并切换捕获模式 */
            dval = HAL_TIM_ReadCapturedValue(&g_tim5_handle, REMOTE_IN_TIMX_CHY);                               /* 读取高电平持续时间 */
            __HAL_TIM_SET_CAPTUREPOLARITY(&g_tim5_handle, REMOTE_IN_TIMX_CHY, TIM_INPUTCHANNELPOLARITY_RISING); /* 切换为上升沿捕获 */

            if (g_remote_sta & 0X10)        /* 确认已完成一次完整的脉冲宽度测量 */
            {
                if (g_remote_sta & 0X80)    /* 已接收到引导码，开始解析数据位 */
                {
                    /* 根据脉冲宽度解析数据位 */
                    if (dval > 300 && dval < 800)           /* 560us±140us，标准数据位'0' */
                    {
                        g_remote_data >>= 1;                /* 数据右移一位，为新位腾出空间 */
                        g_remote_data &= ~(0x80000000);     /* 最高位清0，表示接收到数据位'0' */
                    }
                    else if (dval > 1400 && dval < 1800)    /* 1680us±200us，标准数据位'1' */
                    {
                        g_remote_data >>= 1;                /* 数据右移一位，为新位腾出空间 */
                        g_remote_data |= 0x80000000;        /* 最高位置1，表示接收到数据位'1' */
                    }
                    else if (dval > 2000 && dval < 3000)    /* 2500us±500us，重复码信号 */
                    {
                        g_remote_cnt++;                     /* 按键重复次数递增 */
                        g_remote_sta &= 0XF0;               /* 清空低4位计时器，重置超时计数 */
                    }
                }
                else if (dval > 4200 && dval < 4700)        /* 4500us±250us，NEC协议引导码 */
                {
                    /* 检测到引导码，开始新的数据接收 */
                    g_remote_sta |= 1 << 7;                 /* 设置bit7，标记成功接收到引导码 */
                    g_remote_cnt = 0;                       /* 清零按键计数器，开始新的按键检测 */
                }
            }

            /* 清除上升沿捕获标志，准备下次捕获 */
            g_remote_sta &= ~(1<<4);
        }
    }
}

/**
 * @brief       红外遥控器按键扫描
 * @param       无
 * @return      按键值，0表示无按键，其他值表示对应的按键码
 * @note        扫描红外遥控器是否有有效按键按下
 *              验证地址码和命令码的正确性
 *              需要在主循环中周期性调用此函数
 *              返回值为遥控器的按键编码，具体编码值取决于遥控器型号
 */
uint8_t remote_scan(void)
{
    uint8_t sta = 0;
    uint8_t t1, t2;

    if (g_remote_sta & (1 << 6))    /* 检查是否接收到完整的按键信息 */
    {
        /* 提取并验证地址码 */
        t1 = g_remote_data;                 /* 提取低8位地址码 */
        t2 = (g_remote_data >> 8) & 0xff;   /* 提取地址反码(bit8-15) */

        if ((t1 == (uint8_t)~t2) && t1 == REMOTE_ID)    /* 验证地址码与地址反码互补，且匹配遥控器ID */
        {
            /* 提取并验证命令码 */
            t1 = (g_remote_data >> 16) & 0xff;  /* 提取命令码(bit16-23) */
            t2 = (g_remote_data >> 24) & 0xff;  /* 提取命令反码(bit24-31) */

            if (t1 == (uint8_t)~t2)             /* 验证命令码与命令反码互补 */
            {
                sta = t1;           /* 验证通过，返回有效的按键码 */
            }
        }

        /* 处理接收完成后的状态清理 */
        if ((sta == 0) || ((g_remote_sta & 0X80) == 0)) /* 数据验证失败或遥控器已松开 */
        {
            g_remote_sta &= ~(1 << 6);  /* 清除bit6，清除接收完成标识 */
            g_remote_cnt = 0;           /* 重置按键计数器 */
        }
    }

    return sta;     /* 返回按键码，0表示无有效按键 */
}

/**
 * @brief       红外遥控器扫描任务
 * @param       无
 * @return      无
 * @note        扫描红外遥控器按键并通过printf输出按键信息
 *              需要在主循环或任务调度器中周期性调用
 */
void ScanRemoteTask(void)
{
    uint8_t key;
    char *str = 0;

    key = remote_scan();

    if (key)
    {
        printf("Remote Key: %d, Count: %d\r\n", key, g_remote_cnt);

        switch (key)
        {
            case 0:
                str = "ERROR";
                break;

            case 69:
                str = "POWER";
                break;

            case 70:
                str = "UP";
                break;

            case 64:
                str = "PLAY";
                break;

            case 71:
                str = "ALIENTEK";
                break;

            case 67:
                str = "RIGHT";
                break;

            case 68:
                str = "LEFT";
                break;

            case 7:
                str = "VOL-";
                break;

            case 21:
                str = "DOWN";
                break;

            case 9:
                str = "VOL+";
                break;

            case 22:
                str = "1";
                break;

            case 25:
                str = "2";
                break;

            case 13:
                str = "3";
                break;

            case 12:
                str = "4";
                break;

            case 24:
                str = "5";
                break;

            case 94:
                str = "6";
                break;

            case 8:
                str = "7";
                break;

            case 28:
                str = "8";
                break;

            case 90:
                str = "9";
                break;

            case 66:
                str = "0";
                break;

            case 74:
                str = "DELETE";
                break;
        }

        printf("Remote Button: %s\r\n", str);
    }
}
